// postgres_io_binary.h : binary input and output from postgreSQL database
//

#if !defined(POSTGRES_IO_BINARY_H)
#define POSTGRES_IO_BINARY_H

#include <iosfwd>
#include <string>
#include <vector>
#include <assert.h>

struct pg_binary_header
{
	std::string signature;
	int layout;
	int flags;		
	int header_extra_len;
	explicit pg_binary_header(int flags_ = 0)
		:	signature("PGBCOPY\n\377\r\n"),
			layout(0x01020304),
			flags(flags_),
			header_extra_len(0){assert (signature.length() == 11);}

};

inline bool operator == (const pg_binary_header& h1, const pg_binary_header& h2)
{
	return (h1.signature		== h2.signature			&&
			h1.layout			== h2.layout			&&
			h1.flags			== h2.flags				&&
			h1.header_extra_len	== h2.header_extra_len	);
}

template<typename ostrm>
inline ostrm& operator << (ostrm& os, const pg_binary_header& header)
{
	// 12 byte signature sequence
	os.write(header.signature.c_str(), 12);
	os.write((const char*)&header.layout, 4);
	os.write((const char*)&header.flags, 4);
	os.write((const char*)&header.header_extra_len, 4);
	
	// make sure worked
	assert(os.good());
	
	return os;
}

template<typename istrm>
inline istrm& operator >> (istrm& os, pg_binary_header& header)
{
	char buf [13];
	// 12 byte signature sequence
	os.read(buf, 12);
	buf[12] = '\0';
	header.signature.assign(buf);

	os.read((char*)&header.layout, 4);
	os.read((char*)&header.flags, 4);
	os.read((char*)&header.header_extra_len, 4);
	
	// make sure worked
	assert(os.good());
	
	return os;
}

inline void pg_write_header(std::ofstream& os)
{
	pg_binary_header header;
	os << header;
	
	// make sure worked
	assert(os.good());
}

//***************************************************************************************

//		Functions for io to binary streams in PostgreSQL format

//***************************************************************************************
//_______________________________________________________________________________________

//		utility functions for reading binary to a stream

//_______________________________________________________________________________________
template <typename T, typename ISTRM> T read_binary(ISTRM& istrm)
{
	T temp;
	istrm.read((char*)&temp, sizeof(T));
	return temp;
}

template <typename T, typename ISTRM> T pg_read(ISTRM& istrm)
{
	short len = read_binary<short>(istrm);
	assert(len == sizeof (T));
	return read_binary<T, ISTRM>(istrm);
}

// NB. Static buffer!! Save ASAP
const char* pg_read_str(std::ifstream& istrm);


//_______________________________________________________________________________________

//		utility functions for writing binary to a stream

//_______________________________________________________________________________________
template <typename T> void pg_write(std::ofstream& os, T temp)
{
	short sz = sizeof(T);
	os.write((char*)&sz, sizeof(short));
	os.write((char*)&temp, sizeof(T));
}

void pg_write_str(std::ostream& os, const std::string& str);

//8888888888888888888888888888888888888888888888888888888888888888888888888888888888888888
//
//		Common code to import tuples from the PostgreSQL "sequence" table
//
//
//8888888888888888888888888888888888888888888888888888888888888888888888888888888888888888

//_______________________________________________________________________________________

//		pg_import_tuples

//				template function for reading a list of tuples from a postgres database
//				used by
//						pg_import_hash_tuples
//						pg_import_seq_tuples

//_______________________________________________________________________________________

// helper struct will automatically delete file when exiting scope
struct auto_delete_file_helper
{
	std::string filename;
	auto_delete_file_helper(const std::string& filename_) :filename(filename_){}
	~auto_delete_file_helper(){remove(filename.c_str());}
};


template <typename t_db_binary_import, typename t_ostream>
bool pg_import_tuples(const std::string& ifile, t_db_binary_import& db_import, t_ostream& CLOG)
{
	std::ifstream f_db_import(ifile.c_str(), std::ios::binary| std::ios::in);
	if (!f_db_import)
	{
		CLOG << "ERROR!! Could not open the temp file [" << ifile <<"].\n";
		return false;
	}
	
	auto_delete_file_helper auto_delete(ifile);
	
	if (f_db_import.peek() == EOF)
		return true;

	// make sure file begins with 24 byte header
	const pg_binary_header std_header;
	pg_binary_header test;

	f_db_import >> test;
	if (! (test == std_header))
	{
		CLOG	<< "ERROR!! The file ["
				<< ifile
				<< "] for binary transfer from postgreSQL"
				   " did not begin with the expected"
				   " postgreSQL 7.1 24 byte header.\n";
		return false;
	}


	while (db_import(f_db_import) && f_db_import)
		// loop until returns false
		;
		

	// check not already at EOF
	if (f_db_import.fail())
	{
		CLOG	<< "ERROR!! The file ["
				<< ifile
				<< "] for binary transfer from postgreSQL"
				   " ended prematurely.\n";
		return false;
	}
	
	return true;
}
#endif //POSTGRES_IO_BINARY_H
